#include "ThreadFuncWork.hpp"

void ThreadFuncWork::operator() ()
{
    std::cout << "[Worker]\tInit" << std::endl;

    for ( int i = _task.row_from; i < _task.row_to; i++)
    {
        std::cout << i << "\t" << std::endl;
        
        for ( int j = 0; j < config.width_px; j++)
        {
            Ray ray;
            
            ray.position  = _camera_ptr->PixelTo3d( i,j);
            ray.direction = _camera_ptr->GetDirection( i,j);
            
            ray.origin2d[0] = i;
            ray.origin2d[1] = j;
            
            ray.color.r = 0.0;
            ray.color.g = 0.0;
            ray.color.b = 0.0;

            ray.alpha = 1.0;
            ray.deep  = 1;

            _task.rays.push_back( ray);
            
            processData();
            _camera_ptr->SetPixel( i,j,_result.color);
        }
    }
    std::cout << _nIntersections << std::endl;
}



void ThreadFuncWork::processData()
{
    Ray tmp_ray = *(_task.rays.end()-1);
    _task.rays.pop_back();

    _result.color.r = _result.color.g = _result.color.b = 0.0;
    do
    {
        processRay( tmp_ray );
    
        _result.color.r = ( _result.color.r + tmp_ray.alpha * tmp_ray.color.r) /2;
        _result.color.g = ( _result.color.g + tmp_ray.alpha * tmp_ray.color.g) /2;
        _result.color.b = ( _result.color.b + tmp_ray.alpha * tmp_ray.color.b) /2;
    }
    while ( _task.rays.size() != 0 );
    
    _result.origin2d[0] = tmp_ray.origin2d[0];
    _result.origin2d[1] = tmp_ray.origin2d[1];
}


void ThreadFuncWork::processRay( Ray & ray )
{
    Color   color;
    color.r = color.g = color.b = 0.0;
    
    Vector  hit_point;
    Vector  hit_normal;
    real_t min_distance_2;
    bool    IsIntersection = false;
    int     prim_id = -1;
    
    min_distance_2 = 4000000000;

    for ( uint i = 0 ; i < _scene_ptr->primitive.size(); i++)
    {
        Vector  tmp_hit_point;
        Vector  tmp_hit_normal;

        if ( !_scene_ptr->primitive[ i]->Intersection( ray , tmp_hit_point, tmp_hit_normal))
        {
            continue;
        }
        IsIntersection = true;



        real_t tmp_min_distance = ( tmp_hit_point - ray.position).length2();
        if ( min_distance_2 > tmp_min_distance  )
        {
            hit_point  = tmp_hit_point;
            hit_normal = tmp_hit_normal;
            min_distance_2 = tmp_min_distance;
            prim_id = i;
        }
    }

    if ( !IsIntersection || prim_id == -1 )
    {
        color.r = 0.0;
        color.g = 0.0;
        color.b = 0.0;

        ray.color = color;
        return;
    }
    _nIntersections++;

    for ( uint i = 0; i < _scene_ptr->light.size(); i++)
    {
        Ray from_light_to_hit_point;
        from_light_to_hit_point.position  = _scene_ptr->light[ i].position;
        from_light_to_hit_point.direction = hit_point - _scene_ptr->light[ i].position;
        from_light_to_hit_point.direction.normalize();


        if ( isShadowed( hit_point, from_light_to_hit_point, i , prim_id))
        {
            continue;
        }

        Vector  light_dir  = hit_point - _scene_ptr->light[i].position;
        Vector  camera_dir = hit_point - ray.position;
        
        
        light_dir.normalize();
        camera_dir.normalize();

        Vector h = light_dir + camera_dir; // биссектор между направлениями на свет и камеру
        h.normalize();
        
        Material *material    = &_scene_ptr->primitive[ prim_id]->material;
        Color    *light_color = &_scene_ptr->light[ i].color;

        
        real_t cos_n_to_l = multiply( hit_normal, light_dir);
        //real_t cos_n_to_c = multiply( hit_normal, camera_dir);
        real_t cos_n_to_h = multiply( hit_normal, h);
        
        //real_t  k = cos_n_to_c ;
        real_t k = cos_n_to_l;
        real_t k1;// = std::pow( cos_n_to_h, material->spect_pow  );
        //real_t  k =  cos_n_to_l * cos_n_to_c ;
        
        
        if ( k < 0.0)
        {
            k = 0.0;
            //color.r = 1.0;
        }
        if ( k1 < 0.0)
        {
            k1 = 0.0;
        }
        else
        {
            k1 = std::pow( cos_n_to_h, material->spect_pow  );
        }
    
    //    k1 = 0.0; //FIXME
    
        color.r += light_color->r * ( k * material->diffuse.r + k1 * material->spectecular.r);
        color.g += light_color->g * ( k * material->diffuse.g + k1 * material->spectecular.g);
        color.b += light_color->b * ( k * material->diffuse.b + k1 * material->spectecular.b);
        
    }
    // фоновое освещение
    color.r += config.background_color.r * ( _scene_ptr->primitive[ prim_id]->material.diffuse.r );
    color.g += config.background_color.g * ( _scene_ptr->primitive[ prim_id]->material.diffuse.g );
    color.b += config.background_color.b * ( _scene_ptr->primitive[ prim_id]->material.diffuse.b );

    ray.color =  color;
}

bool ThreadFuncWork::isShadowed( Vector & hit_point, Ray & from_light_to_hit_point, uint i, int prim_id)
{
    if ( config.shadow == false)
    {
        return false;
    }
    bool Shadowed = false;
    real_t dist_from_hit_point_to_light_2 = ( hit_point - _scene_ptr->light[ i].position).length2();
    for ( uint j = 0; j < _scene_ptr->primitive.size(); j++)
    {
        if ( j == (uint)prim_id)
        {
            continue;
        }
        Vector tmp1, tmp2;
        
        if ( _scene_ptr->primitive[ j]->Intersection( from_light_to_hit_point, tmp1, tmp2))
        {
            if ( ( tmp1 - _scene_ptr->light[ i].position).length2() < dist_from_hit_point_to_light_2)
            {
                //std::cout << "Shadow" << std::endl;
                Shadowed = true;
                break;
            }
        }
    }
    return Shadowed;
}

void ThreadFuncWork::Init ( Task_t &task, Scene &scene, Camera &camera )
{
    _task        = task;

    _camera_ptr  = &camera;
    _scene_ptr   = &scene;
    
}

ThreadFuncWork::ThreadFuncWork()
{
    _nIntersections = 0;
}

ThreadFuncWork::~ThreadFuncWork()
{
    _nIntersections = 0;
}
